Managing Input Forms "Will that Gold product ever get finished?" A. Spouse, 1995 Introduction Input/Output, or IO for short, is the heart of any program. As you are about to discover, it is also the heart of Gold! The Gold units which support IO forms are GOLDIO, GOLDIO2 and GOLDIO3. These units offer a full set of sophisticated input tools including the following: 27 different field types support string, number and date input, along with radio buttons, checks boxes, lists and push buttons. Automated input validation, e.g. only accept numbers in the range 16 to 60; only accept dates in 1995; etc. Flexible picture fields allowing users to input telephone numbers, part numbers, etc. A bevy of user hooks allowing the developer to fine tune the form's behavior. Forms can be displayed in draggable windows. Exotic fields such as spinners and drop-down calendars are available. All validation and error messages are customizable allowing easy internationalization (that's localization for the politically correct among us). Multiple simultaneous forms are supported. For example, a secondary form can be displayed when a user clicks on a button in the main form. Fully supports the mouse. All colors are customizable. Fields can be grayed (or disabled) based on the values in other fields. Oh, that's enough, you get the idea. Building a Form -- A Quick Recipe Time for an analogy. You don't need to be an accredited chef to cook breakfast (unless you happen to be French). The same goes for IO forms -- although there is a great deal that you need to learn to become a Form Jedi, you can still create very useful and functional forms by following an essentially simple recipe. Before you delve into the form creation process, you need to understand some of the basic principles. A form is a collection of fields bound together to give the user a single input screen or dialog. Fields have various uses. You might use a field to record a person's name or zip code, allowing the end user to type text directly into the field. Other fields don't accept normal data entry, such as radio buttons or lists -- the user makes a choice from a predefined set. If you follow a simple recipe and take the steps in the correct order, you can create a form in just a few minutes. Here is the recipe: Decide what data you want to garner from the user, and what the data types will be. e.g. 5 strings for the address, an integer for the age, and a string for the sex. Decide on the basic layout of the form. e.g. the form might have 5 fields (one below the other) to enter the user's name and address, and a numeric field to enter the person's age. Finally, there might be OK and Cancel buttons on the form. Activate a form. A Gold form can support multiple forms, and at any one time, a single form will be the active form, i.e. will have focus. All IO procedures and function calls are directed at the active form. Add the fields to the form. At this stage, Gold doesn't care about each field's properties -- just the order of the fields and the (X,Y) coordinate of each field. Define the properties of each field. You might set the first field, for example, to be a string field that accepts up to 30 alpha numeric characters. You will also tie fields to specific variables. When the user enters data in the field, the variable is automatically updated with the value displayed in the field. Add optional labels and messages to each field. e.g. You might add the left-justified label "Name:" to the first field . Assign appropriate values to the variables referenced by the form. Run the form and wait for user input. If the form is no longer required dispose of the fields. Figure 16.1 is the form created in DEMIO1.PAS. On the following page is a full listing of the program along with annotations showing each of the major steps in the recipe. Figure 16.1 A Basic Form Let's analyze a few lines of code from the example. KwikAddField(1, 25,9); KwikAddField(2, 25,11); These lines add fields to the form. The first field is located at coordinates (25,9) and the second field is located at coordinates (25,11), i.e. two lines below the first line. StringField(1, Name, '******************************'); SetLabel(1, LabelLeft,LabelLeft,'Full name:'); These two lines define the properties of field one. The field is a string field, i.e. a standard field where the user can enter text or numbers. The variable associated with the field is named NAME, and this variable will be assigned the string that the user enters. The third parameter identifies the string picture. An asterisk "*" indicates that any alphanumeric character will be accepted in that position. The other picture characters are "!" which forces alphas to upper case, "#" which accepts only numbers, and "@" which accepts letters and punctuation. In this case, the field will be 30 characters wide because there are 30 asterisks in the picture. The second line adds a left justified label 'Full name:' to the field -- you can see this label in Figure 16.1. StringField(2, Tel, '(###) ###-####'); SetLabel(2, LabelLeft,LabelLeft,'Tel:'); These two lines define the properties of field one. This field is also a string but the picture is formatted for telephone number input (see Figure 16.1). The tel string will be updated with the user input. The second line adds a left justified label of 'Tel: ' to the field. ButtonField(5,' OK ',finished); This line sets the fifth field to be a push button with the text OK. When the button is pressed, the form input will be terminated and the value Finished will be returned by the EditForm function. Result := EditForm(1); This line instructs Gold to display the form and wait for the user to conclude the input session. The function returns a value of type gAction to indicate how the user ended the session. In this example, the function might return Finished if the F10 key was pressed or the user selected the OK button, or Escaped if the user pressed the Esc key or selected the Cancel button. If you are the kind of programmer who likes to learn by example (that's all of you, by the way), investigate the demo files DEMIO2.PAS through DEMIO5.PAS. Each demo builds on the previous one, and adds another degree of sophistication to the form. By the last demo, the form is displayed in a window with field messages displayed on the status bar, radio buttons and ... well, just take a look for yourself. Configuring the Form(s) Having learned the instant form recipe, it's time to take a more detailed look at the entire form management process. RTFM. Creating Multiple Forms Gold allows an application to have multiple forms defined at one time, i.e. within the same scope. For example, form 1 might be a customer name and address form, and form 2 might be an order entry form. If you want to use more than one form at a time, you must instruct Gold to create the number of forms you will need using the following function: CreateForms(Count:byte); Instructs Gold to allocate enough memory for Count forms. Note that Count must be in the range 1 to MaxForms (which is defined in GOLDIO as 10). This should be called before any other IO procedures and functions. If you call any Gold IO procedure or function before you call CreateForms, Gold will assume that you will only be using one form, and will call CreateForms(1) internally. That is why the example on page 17-4 didn't use a CreateForms statement. It is important to realize that you do not need to create more than one form just because you intend to use more than one type of form in your application. You can use the same form for more than one purpose if you dispose of the first set of fields and redefine a new set of fields prior to displaying the revised form -- more about field disposing later. If you want to call CreateForms more than once in an application (and this will be rare), you must ensure that the you call DisposeForms before calling CreateForms a second time. Giving a Form Focus If you don't make a call to CreateForms, then you don't need to worry about form focus. If your application creates more than one form, read on. The notion of focus is used in many parts of Gold: the top window on the desktop has focus, and in list applications, a specific single or double linked list has focus. The same goes for input forms. If you have more than one form in an application, Gold needs to know which form to work on, i.e. which form has focus. The following procedure sets the focus to a specified form: ActivateForm(FormNo:byte); Instructs Gold to direct all IO procedures and functions toward the specified form. The form must fall within the range 1 to the value specified in the call to CreateForms. The following code fragment shows how ActivateForm might be used to configure multiple forms: begin CreateForms(4); ActivateTable(1); ...{work on form 1} ActivateForm(2); ...{work on form 2} end; Creating Dialogs, i.e. Forms in Windows If you are using forms (in windows) on the desktop, then refer to the section Launching Forms on the Desktop at the end of this chapter. The text below refers to forms created outside of the desktop environment. By default, the Gold form routines are configured to work full screen. If you want to display a form in a window you must advise Gold that the form will be displayed in a window before calling the field definition functions like AddField, StringField, etc. Use the procedure SetFormWindow (defined below) to configure the form to run in a window. SetFormWindow(X1,Y1,X2,Y2,style:byte); Instructs Gold to create a form in a window. The first four parameters define the window coordinates, and the fifth parameter defines the window style. Having set the form window, the function FormWinNum will return the window handle (or number). You can customize (and write to) a form window, just like any standard Gold window. The following code fragment is from the demo file DEMIO10.PAS: procedure SetFields; {} var I : Integer; begin CreateForms(1); ActivateForm(1); {define the form's window} SetFormWindow(15,4,67,22,1); WinSetTitle(FormWinNum,' Customer Comments '); WinSetType(FormWinNum,WMove); WinSetShowNum(FormWinNum,false); KwikAddField(1,10,2); KwikAddField(2,10,14); KwikAddField(3,35,14); KwikAddField(4,35,16); ... end; Gold will dispose of the window automatically when you call DisposeFields. Customizing a Form's Appearance and Behavior Overriding the Form Navigation Keys Most of a form's characteristics are controlled by the individual fields in a form. The keys used to jump from field to field, however, are set for the entire table. By default, the keystrokes to shift to the next field up, down, left and right are the Up cursor, Down cursor, Ctrl-Left cursor and Ctrl-Right cursor respectively. The de facto standard of using the Tab and Shift-Tab keys to move to the next and previous fields are also supported. The F10 key is assigned to be the finished key, Esc is the escaped key, and Ctrl-E is the erase field key. You can customize these keys using one of the following three procedures: AssignActionChars(Nxt,Prv,U,D,L,R,Fin,Esc,E: word); Defines the keys the user will use to navigate the form. These nine word parameters represent the key codes to move to the next field, the previous field, the field above the field below, the field to the left, the field to the right, the key to finish editing, the key to abort editing and the key to erase the entire field contents, respectively. Here is a little tip; if you pass a keycode of zero, the keystroke assignment for that key will not be changed. This provides a quick and easy way to change some, but not all, of the key codes. For example, the following statement will only redefine the up, down, left and right keys: AssignActionChars(0,0,278,288,294,275,0,0,0); AssignFinishChar(W:word); Provides a quick way to change the finished editing key, which defaults to F10. AllowEsc(On:boolean); Controls whether the user can Esc from the form. Pass TRUE to enable the Esc key, and FALSE to disable it. Customizing Colors There are many different colors that might be used in a form. In fact there are 37 tint elements that affect forms and they are defined as follows: IOEditErase, IOEditNorm, IOEditHi, IOEditOff, IOButtonNorm, IOButtonNormHot, IOButtonHi, IOButtonHiHot, IOButtonOff, IOButtonDef, IOButtonDefHot, IOMessage, IOLabelNorm, IOLabelNormHot, IOLabelHi, IOLabelHiHot, IOLabelOff, IOWinBorder1, IOWinBorder2, IOWinTitle, IOWinBody, IOWinIcons, IOWinBorderOff, IOCheckArea, IOChoiceHi, IOChoiceHiHot, IOChoiceNorm, IOChoiceNormHot, IOChoiceOff, IOIcons, IOIcons2, IOListHi, IOListHiHot, IOListNorm, IOListNormHot, IOListOff, IOListScroll If you want to change the color of all the forms in your application, then you should call the GoldSetColor function and modify the Gold defaults, prior to calling any of the IO procedures and functions. If you want to change the colors for a specific form, then call the IOSetColor function which is defined as follows: IOSetColor(A:TintElement;C:byte); Changes the color of a single element of the active form. Listed below is an extract from the demo file DEMIO6.PAS which customizes the colors in a form. procedure CustomizeColors; {} begin IoSetColor(IOEditErase,WhiteonRed); IoSetColor(IOEditNorm,WhiteOnMagenta); IoSetColor(IOEditHi,YellowOnGreen); IoSetColor(IOEditOff,LightgrayOnMagenta); IoSetColor(IOButtonNorm,WhiteOnBlue); IoSetColor(IOButtonNormHot,YellowOnBlue); IoSetColor(IOButtonHi,WhiteOnRed); IoSetColor(IOButtonHiHot,YellowOnRed); IoSetColor(IOButtonOff,WhiteOnBlack); IoSetColor(IOButtonDef,LightgrayOnBlue); IoSetColor(IOButtonDefHot,LightcyanOnBlue); IoSetColor(IOMessage,WhiteOnBlack); ... end; { CustomizeColors } Understanding Field Rules The field rules define the characteristics of the field. For example, whether the field is right justified or left justified. The rules are stored for each field individually, and the programmer can change any or all of the rules as desired. There are five different field rules, defined as follows: AllowNull - indicates the user is allowed to leave the field empty. SuppressZero - the field is displayed as blank in a number field if the value is zero. RightJustify - the field grows from right to left rather than the normal left to right. EraseDefault - when the user enters the field, the default (or previous value) is erased if the user presses a normal alphanumeric key. JumpIfFull - move to the next field to the right if the field is filled with data, i.e. if the user has entered sufficient characters to fill the field. These five values are defined as constants in GOLDIO.PAS, and can be summed to combine field rules. For example, a field may be defined as having rules AllowNull + SuppressZero. If one of the constants is omitted from a field rule's declaration, then that rule will not be enforced. In the example just mentioned, for example, the field would not be right justified. If you want none of the rules to apply, then specify the constant NoRules as the only field rule. To set the default rules for an entire form, call the following procedure before adding the fields: SetDefaultRules(Rules:word); Defines the default rules that will be applied to any fields subsequently created with AddField. Miscellaneous Form Defaults The following procedures and functions are used to set other default characteristics of a form: SetMessageXY(X,Y:byte; InWindow: boolean); Defines the location of where field messages will be displayed. These messages provide the user with information about the field with focus. For example, a message might be "Enter an age in the range 15 to 65". If the form is being displayed in a window, and the message will be displayed inside the window, set the InWindow parameter to TRUE, otherwise set it to false. SetInsertMode(On:boolean); Controls whether the user is initially in insert or overtype mode. Pass TRUE to start the form in insert mode. SetValidation(Val:gValidate); Gold offers automatic field validation on numeric and date fields. The validation checks can be performed when the user moves from one field to the next, or when the user attempts to finish input (by clicking on the OK button, or pressing F10, for example). The procedure accepts one of the following two values: gValidate = (ValidatebyField,ValidateAtEnd); Placing Fields in a Form Having defined the form's main properties, you need to place the fields on the form. At this point Gold doesn't need to know anything about the field except where it is positioned, and which fields will be adjacent to it. AddField(FieldID,DefU,DefD,DefL,DefR,DefX,DefY:byte); Adds a field to a form. The first parameter identifies the logical field number. This must be unique, and it is recommended that the fields are added in ascending numerical order. This same field identifier is used in subsequent calls to change the field's properties. The next four parameters identify the field ID of the fields which will be jumped to when the user attempts to navigate upward, downward, leftward and rightward, respectively. The final two parameters identify the (X,Y) coordinate of the first character of the field. KwikAddField(FieldID,DefX,DefY:byte); Performs the same function as AddField but leaves Gold to assign the field jumps. Upward and leftward will jump to the field whose ID is one less than the current field. Downward and rightward will jump to the field whose ID is one greater than the current field. Don't use this function to add the last field in a form, since there will not be a field with an ID greater than the current field -- use KwikAddLastField below. KwikAddLastField(FieldID,DefX,DefY:byte); Adds the last field to a form. See KwikAddField above. One family of fields, invisible fields, cannot be added with any of the three functions described above. They are not visible and so do not have (X,Y) coordinates, and the user never lands on or edits them. These fields are referred to as HotKey fields, and are added to the form using the following procedure: AddHotKeyField(FieldID:byte; Key:word; Action:gAction); Adds a hotkey (non-visible) field to a form. The first parameter identifies the field ID, the second parameter the word value of the hotkey, and the third value identifies the action code invoked when the user presses the specified key. Listed below is the enumerated type gAction defined in GOLDIO.PAS. gAction = (None,NextField,PrevField,NextForm,PrevForm, Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99, Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5, Cancel6,Cancel7,Cancel8,Cancel9,Escaped); The following three functions apply to all visible fields, and can be used to set field messages, labels and hotkeys: SetMessage(FieldID,X,Y:integer; Str : string); Defines the message which will be displayed when the field has focus. The message location is controlled by using the procedure SetMessageXY, discussed earlier. SetLabel(FieldID,X,Y:integer; Str : string); Identifies a label that will be printed when the form is displayed. In most forms each field will have a label. Although you can specify an explicit XY location, to literally put the label anywhere on the form, in most situations, use the constant LabelLeft or LabelTop to instruct Gold to automatically position the label relative to the field. SetHK(FieldID:byte; Hotkey: word); Defines the keystroke which a user can press to automatically jump to the field. The keystroke should normally correspond to a highlighted letter in the field label. For example, if the letter A is highlighted, the hotkey would be assigned as AltA, i.e. 286. Individual Field Rules SetDefaultRules defines the default field input rules for a form. Individual field rules can be specified using the procedure FieldRules. In addition to identifying the field rules as discussed earlier, the FieldRules procedure can control the specific characters which the user will be allowed to input. These character rules go beyond the simple field masks such as @, *, ! and #. For example, the only valid characters for a field that identifies a warehouse part number may be "A B C 1 2 3 4 5 6". Alternatively, a field that is a filename may allow any alphanumeric character except the * and ? characters. Both of these conditions can be accommodated using the FieldRules procedure defined as follows: FieldRules(FieldID:byte;Rules:word;AChar:IOcharset; DChar:IOcharset); Defines the field rules for a specific field. The second parameter identifies the field rules and can be any combination of AllowNull, SuppressZero, RightJustify, EraseDefault and JumpIfFull. The third parameter is of type set of char and identifies the allowable characters. These characters should be defined within square parentheses, e.g. ['A'..'C','1'..'6']. The constant [NoChar] can be used to indicate that no special character restrictions apply. The fourth parameter is also of type set of char and identifies disallowed characters, e.g. ['*','?']. If no characters are to be disallowed, use the constant [NoChar]. Controlling Field States The following two procedures are used to set and get the state of a field, i.e. whether the field is enabled, disabled or hidden. As you might imagine, hidden fields are not visible and cannot have focus. Disabled fields, however, are visible but are displayed in a grayed style, and cannot have focus. The gActiveState is an enumerated type defined in GOLDIO.PAS as follows: gActiveState = (FldOff, FldOn, FldHidden); FieldSetState(FieldID:byte; State:gActiveState); Sets the state of the field to enabled, disabled or hidden. FieldGetState(FieldID:byte):gActiveState; Returns the state of the specified field. Defining Each Field's Properties Typical fields allow the user to enter text or numbers, but there are also radio buttons, check boxes, push buttons, etc. Having placed the fields on the form (with AddField or KwikAddField), you must now define each field's properties. For example, field 1 might accept text input, and field 2 might be a push button, etc. Gold includes 27 different field types for your programming pleasure and they are organized into the following categories: String Fields Number Fields Date Fields Push Buttons Radio Buttons and Check Boxes List Fields Multi-Line Edit Fields Hot Spots Understanding Field Formats All the standard fields (i.e. string, number and date fields) accept a string variable called DefFormat. This string uses Gold's special mask characters to define the field's width and optionally any mask characters, i.e. characters which are displayed in the input field, but are non-editable, and the cursor automatically jumps past these mask characters. There are four pre-defined format characters in Gold: # Allow 0..9, '.','-', and 'e' for scientific @ Only letters of the alphabet and punctuation * Any character the user can find! ! Like @, but all characters are converted to uppercase Any other characters identified in the field's format string are treated as fixed and for display only. For example, if the input field is a telephone number, then the format might be defined as '(###) ###-####'. The user will only be allowed to input numbers, and after typing three numbers, the cursor will jump three positions to the right (to the next # character). As you may recall, every field is associated with an individual variable. Note that this variable is not updated with the formatting characters. For example, if the input field on the screen displayed "(409) 737-5472" the variable would be updated with the string "4097375472. This saves space if the variables are stored in a database. The GoldSTR unit contains the function PicFormat which is passed a string and a picture, and which returns the combined formatted string. String Fields The string fields form the backbone of user input forms as they are used to gather string data from the user, e.g. names, addresses, part numbers, etc. Two different string field types are provided for your coding pleasure: a StringField is of fixed width and supports the formatting characters; the ScrollField allows the user to input a string that is wider than the displayed field width, but does not support special formatting characters. The fields are defined as follows: StringField(FieldID:byte;var Strvar:String;DefFormat:string); Basic input field allowing the user to input a string. The width of the field is defined by the DefFormat parameter. For example, a twelve character name field might be defined with a DefFormat of '************' or a shortcut would be to use the Replicate function, i.e. Replicate(12,'*'); ScrollField(FieldID:byte; var Strvar:string;FieldL,MaxL:byte); Accepts string input and provides scrolling features, allowing the user to input more characters than the visible field width. Use this field type when you have limited space on the form, or when you want to allow the user to enter a large number of characters, and don't want a humongo field displayed. The last two parameters define the visible field width and the maximum number of characters which the user may input, respectively. Number Fields As you might imagine, number fields should be used when you want the user to input a number. A variety of different field types are provided. The ByteField, WordField, IntegerField, LongIntField procedures all accept whole numbers, and are defined as follows: ByteField(FieldID:byte;var Bytevar:Byte;DefFormat:string; Min,Max:byte); WordField(FieldID:byte;var Wordvar:Word;DefFormat:string; Min,Max:word); IntegerField(FieldID:byte;var Integervar:Integer;DefFormat:string; Min,Max:Integer); LongIntField(FieldID:byte;var LongIntvar:LongInt;DefFormat:string; Min,Max : LongInt); The DefFormat passed to these four fields defines the field width and although mask characters are supported, you will typically use the # character to indicate that numbers should be input. Each of the four field types accepts Min and Max numbers indicating the valid number range that you want to impose on the user. For example, an age field might be set to accept ranges from age 12 to age 65. If one of the Min or Max values is non zero, Gold will automatically impose the range restrictions. The brighter among you may have deduced that the Min and Max should both be set to zero when you want to accept any number supported by the variable type. Some modern applications provide spinner icons to allow the user to quickly scroll through a number series. Gold provides the SpinLongField function for this purpose (defined below). If you want the user to input a whole number within a specific range, and offer spinners, then use SpinLongField, but remember that the variable must be of type LongInt. SpinLongField(FieldID:byte; var LongIntvar:LongInt;Width:byte; Min,Max,Increment : LongInt); Gold provides two field types for the entry of real numbers. Use FixedRealField to get input when there are a fixed number of decimal places required. For example, a FixedRealField would be ideal for inputting dollar values where two decimal places would be used for the cents. If, on the other hand, the number of decimal places is at the discretion of the user, use a RealField, because the user can enter the decimal point (or whatever obscure character your country may utilize for this purpose!) where appropriate. RealField(FieldID:byte;var Realvar:extended;DefFormat:string; Min,Max:extended); FixedRealField(FieldID:byte; var Realvar:Extended;Whole,DP:byte; Min,Max:extended); As the name suggests, the SpinRealField (defined below) provides real field input with spinners. The last parameter defines how much the input number will be incremented or decremented when the spinner is clicked. SpinRealField(FieldID:byte; var Realvar:extended;Whole,DP:byte; Min,Max,Delta:extended); Date Fields While Gold won't make you more attractive to the opposite sex, it will allow you to get dates from your users! Gold supports eight different date formats, which are identified using the gDate enumerated type defined in GoldDate as follows (see Chapter 19): gDate = (MMDDYY,MMDDYYYY,MMYY,MMYYYY,DDMMYY,DDMMYYYY, YYMMDD,YYYYMMDD); You can use one of four different field types to garner date information from the user. The traditional DateField allows the user to input a date using the keyboard, the traditional way. The SpinDateField adds spinners to the standard date field, allowing the user to easily scroll backwards and forwards through dates. If you want the user to optionally select the date from a pop-up calendar (and let's face it, who wouldn't?!), use the DropDateField. This field sports an arrow to the right of the field. When the user clicks on the arrow, a calendar is displayed. If you want spinners and a pop-up calendar, you guessed it, use the SpinDropDateField. These four fields are defined as follows: DateField(FieldID:byte;var Datevar:Dates;DateFormat:gDate; DefFormat:string;Min,Max : Dates); SpinDateField(FieldID:byte; var Datevar:Dates; DateFormat:gDate;DefFormat:string; Min,Max : Dates); DropDateField(FieldID:byte; var Datevar:Dates; DateFormat:gDate;DefFormat:string; Min,Max : Dates); SpinDropDateField(FieldID:byte;var Datevar:Dates; DateFormat:gDate; DefFormat:string; Min,Max : Dates); Each date field takes a variable of type dates. As with the number fields, the Min and Max variables define the earliest and latest dates which you will allow the user to input. (These dates are Julian; refer to Chapter 19 to find out more about Julian dates.) Specify a Min and Max of zero if you want to accept any valid date. Note that Gold will always ensure that the date is valid, e.g. November 31st, 1956 would not be accepted. Each of the four date fields accepts a DefFormat string. This parameter is primarily offered for backward compatibility and to provide support for other date formats not intrinsically supported by Gold. Typically, pass a null string as the DefFormat and Gold will assign the appropriate picture characters based on the DateFormat specified. Push Buttons Push button fields don't accept direct user input, i.e. you can't enter data into a "button". Button fields, therefore, are not linked to a variable. Each button added to a form has an action code of type gAction. When the user presses the button, the button's action code is processed. This results in the form editing session finishing, and the action code returned by the form is the action code assigned to the button. You may recall that gAction is defined in the GoldIO unit as follows: gAction = (None,NextField,PrevField,NextForm,PrevForm, Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99, Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5, Cancel6,Cancel7,Cancel8,Cancel9,Escaped); Buttons can be added to a form using ButtonField or ButtonDefaultField, which are defined as follows: ButtonField(FieldID:byte; Face:string; Action:gAction); ButtonDefaultField(FieldID:byte; Face:string; Action:gAction); The second parameter identifies the text that will be displayed on the button, and the third parameter is the gAction code that will be returned by the form when the button is pressed. Use the ButtonDefaultField to identify a button which will be "pressed" when the user presses the Enter key -- there should only be one default button field on a form. The ButtonChangeSettings procedure (defined below) can be used to dynamically change the face and action code of a button. This might be used, for example, to change an "Edit" button to a "Save" button when the button is pressed. ButtonChangeSettings(FieldID:byte; Face:string; Action:gAction); The following code fragments are from the demo file DEMIO13.PAS which uses buttons to help the user tag and untag selections in another field. procedure SetFields; {} begin ..... KwikAddField(1, 1,2); KwikAddField(2, 6,19); KwikAddField(3, 18,19); KwikAddField(4, 33,19); KwikAddField(5, 49,19); KwikAddLastField(6, 62,19); AssignHindHook(HindHook); {The List} FillTheList; ConfiguretheListFormat; ListAssignSLL(ListFormat,SourceList); WrapListField(1,75,1,14,ListFormat); {Buttons} ButtonField(2,' ~T~ag ',Stop1); ButtonField(3,' Tag ~A~ll ',Stop2); ButtonField(4,' ~U~ntag all ',Stop3); ButtonDefaultField(5,' ~O~K ',finished); ButtonField(6,' ~C~ancel ',escaped); SetHK(2,276); {Alt+T} SetHK(3,286); {Alt+A} SetHK(4,278); {Alt+U} SetHK(5,280); {Alt+O} SetHK(6,302); {Alt+C} end; {SetFields} repeat DisplayAllFields; EditAction := EditForm(1); case EditAction of Stop1: begin ItemIsTagged := SLLGetTagState (ListFormat.ActiveNode); SLLSetBit(SLLNodePtr(ListFormat.ActiveNode), TagBit, not ItemIsTagged); SLLSetBit(SLLNodePtr(ListFormat.ActiveNode), ColBit, not ItemIsTagged); end; Stop2: begin SetTagAll(ListFormat,true); end; Stop3: begin SetTagAll(ListFormat,false); end; end; {case} CheckTagButton; until EditAction in [Finished,Escaped]; Note that the form is displayed in a repeat loop -- every time a button is pressed the EditForm function (discussed later) returns the value of the button pressed by the user. Although the user isn't aware of it, the form is being closed and opened many times during the edit session. Radio Buttons and Check Boxes Two common elements of contemporary input forms are check boxes and radio buttons. Both these objects provide ways of choosing items from collections of options. A check box field provides a list of options with each item having its own check box displayed to the left of the item, e.g. [X] Toast. Any number of items in the list can be selected. An item's selection status is toggled by hitting the space bar (even with your forehead) or by clicking the mouse cursor on it. Selected items have an X in the adjacent check box. Radio buttons are similar to check boxes, except that only one item can be selected at any one time. When an item is selected, the previously selected item is deselected. The selected item has a dot in the "button" next to it, e.g. (.) Ham Sandwich. Radio Buttons To define a radio button field, you must define the field using RadioField, and then call RadioAddItem once for each option in the field. These procedures are defined as follows: RadioField(FieldID,width,depth:byte; var SelectedItem:byte); The Width and Depth fields define the overall dimensions of the radio button field, in characters and lines, respectively -- this is analogous to defining the groupbox in Windows (just a note for you Delphi jocks). The SelectedItem variable indicates which radio button is selected by the user. Be sure to assign a suitable value to this variable before running the form. RadioAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string; ItemHK:word); Defines a specific item in a Radio field. Note the ItemX and ItemY parameters define the position of the item relative to the topleft of the Radio field (not the top left of the form). CheckFields To define a check button field, you must define the field using CheckField, and then call CheckAddItem once for each option in the field. These procedures are defined as follows: CheckField(FieldID,width,depth:byte); The Width and Depth fields define the overall demensions of the radio button field, in characters and lines, respectively. CheckAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string; ItemHK:word;var gResult:boolean); Defines a specific item in a Check field. Note the ItemX and ItemY parameters define the position of the item relative to the topleft of the Radio field (not the top left of the form). The gResult variable is set to true when the item is checked, and false when it is not checked. Disabling Radio or Check Button Items If you want one element of a radio button or check box field to be disabled, use CheckRadioSetActive which is defined as follows: CheckRadioSetActive(FieldID,ItemNum:integer;IsActive:boolean); Sets the enabled state of an individual radio button or check box item. The ItemNum parameter represents the individual element whose state will be changed -- the first added item has a value of 1, the second a value of 2, and so on. The following code fragment is from the demo file DEMIO6.PAS, and show how to construct radio button and checkbox fields: {Radio Buttons} RadioField(9, 25,4,Cust.Radio); RadioAddItem(9, 1,1, 'Corporation','',0); RadioAddItem(9, 1,2, 'S Corporation','',0); RadioAddItem(9, 1,3, 'DBA','',0); RadioAddItem(9, 1,4, 'Individual','',0); SetLabel(9,LabelTop,LabelTop,'Business ~T~ype'); SetHK(9,276); {Check Boxes} CheckField(10, 25,4); CheckAddItem(10, 1,1, 'Small Business','',0, Cust.Check1); CheckAddItem(10, 1,2, 'Minority Owned','',0, Cust.Check2); CheckAddItem(10, 1,3, 'English Owned','',0, Cust.Check3); CheckAddItem(10, 1,4, 'Woman Owned','',0,Cust.Check4); CheckRadioSetActive(10,3,false); SetLabel(10,LabelTop,LabelTop,'~B~usiness Categeory'); SetHK(10,304); List Fields Gold supports basic list fields that can be created and defined in a couple of fields, as well as gut-busting lists which have all the power of the list engine defined in the GoldLIST unit. Basic List Fields Creating a list field is a two step process. First use ListField (described below) to define the overall dimensions of the list. ListField(FieldID,width,depth:byte; var SelectedItem:integer); Identfies the width and depth of a list field. The SelectedItem variable will be updated with the one-based node number of the selected item. Individual items in the list can be added using the ListAddItem function, which is defined as follows: ListAddItem(FieldID:integer; Str:string); Adds the specified string to the list. That's all there is to it. If you want to add a set of fields in one fell swoop, use the ListKwikAddItem as follows: ListKwikAddItem(FieldID:integer; Str:string); Adds a series of items to a list field. All the items are passed in a single string, and the items are separated with the split bar character "|". The following code fragment shows KwikListAddItem in use: SpinDropListField(1,25,PrintInfo.TypeFaceID); ListKwikAddItem(1,'Adelaide|Arial MT|Bookman|Courier' ListKwikAddItem(1,'Dom Casual |Freestyle|Script'); ListKwikAddItem(1,'Helvetica|Juniper|Kidnap|Lithos'); ListKwikAddItem(1,'Light|Times Roman|Zap Dingbobs'); In addition to the standard rectangular list box, Gold can display lists using formats similar to the Window's ComboBox. Lists can be displayed in a drop-down box, as a spinner field, or both features can be combined into a single spin drop list field. These different formats can be implemented by using one of the following three procedures in place of the standard ListField procedure: DropListField(FieldID,width:byte; var SelectedItem:integer); SpinListField(FieldID,width:byte; var SelectedItem:integer); SpinDropListField(FieldID,width:byte; var SelectedItem:integer); Use the EditDropListField (defined below) if you want the user to be able to type text into the field or optionally select the text from a drop-down list box. EditDropListField(FieldID:byte; var Strvar:string; FieldL,MaxL:byte); To change the contents of a list field "on the fly", you should call ListRebuild (defined below) and pass the list contents as a single string using the KwikAddField format. ListRebuild(FieldID:integer; Str:string); If you want to get the literal string of the list items selected by the user, you can use the ListGetString function which is defined as follows: ListGetString(FieldID:integer; EntryNo:integer): string; Power List Fields To display lists in forms which have advanced features such as tagging, wrapping, etc., you'll need to create a string linked list, and then use one of the power list fields. The power list fields use the list engine defined in the GoldLIST unit. We suggest you also read Chapters 14 and 15 to gain a thorough understanding of Gold's power field support. To display a list defined in an StrLL (a simple string linked list), first create the field using ListField, SpinDropListField (or any of the other variations) and then use ListAssignStrLL to define the list contents. ListAssignStrLL(FieldID:integer; var SL:StringLL); To change the list contents "on the fly", use the following procedure in preference to ListRebuild: ListUpdateStrLL(FieldID:integer; var SL:StringLL); The most powerful list fields provide all the functionality of Gold's list windows as a field in a form. Before adding the list field to the form, using one of the three procedures listed below, first create a linked list and a ListCfg variable following the same principles as outlined in Chapter 14. WrapListField(FieldID,Colwidth,ColCount,RowCount:byte; var ListDetails: ListCfg); GridListField(FieldID,width,depth:byte;var ListDetails: ListCfg); BrowseField(FieldID,width,depth:byte; var ListDetails: ListCfg); Use the following two functions to ascertain the user's selection from one of these power fields, along with the last key that was pressed when the list had focus in the form: ListGetActivePick(FieldID:integer): integer; ListLastKey(FieldID:byte):word; Multi-Line Edit Fields Chapter 15 described how to use Gold's editor to add editing capabilities to an application. The same editor engine can be accessed from a form by using a memo field. It goes without saying that you need to read Chapter 15 to get the most out of the memo field. To add a memo field to a form, create a memo data source, such as a linked list, create and set a MemoCfg variable and then call MemoField, which is defined as follows: MemoField(FieldID,width,depth:byte;var Memo:MemoCfg); Hot Spots When is a field not a field? When it's a hotspot! A hot spot field is really an invisible button. When you click on a hot spot, Gold will end the form edit session and return the gAction code assigned to the hotspot. Define a hot spot field using the following procedure: HotspotField(FieldID:byte; Width,Depth: byte; Action:gAction); Although the field seems to have little value, you can use it in conjunction with plain text to create custom buttons. For example, you might draw four different boxes on the form and write some text within each box. These text boxes are not active parts of the form, but by adding hotspots which overlay the precise area of the box, you can respond to user clicks in the rectangle. Running The Form Using EditForm Once the form has been fully defined, use the EditForm function to pass control to the user and let them get on with the input. EditForm is defined as follows: EditForm(StartField:byte):gAction; Displays the active form and allows the user to edit data in the fields. The single passed parameter indicates the FieldID of the field which will be initially focused when the edit session begins. The function returns a value of type gAction indicating how the edit session was terminated. This may be Finished, Escaped, or any of the codes assigned to buttons on the form. You should check the value returned by EditForm to determine whether the user was aborting, or the user wanted the edits committed. The following code fragment illustrates this technique: if EditForm(1) = Finished then SaveTheEdits else IgnoreTheEdits; The function FieldWithFocus returns the FieldID of the field with focus. If you are using EditForm in a loop, use FieldWithFocus to determine which field to give focus on the next iteration. The following code fragment (extracted from DEMDB1.PAS) demonstrates this technique: ActiveField := 13; repeat RecNum := X; if (DbGetNumRecs > 0) and (X > 0) then DatabaseToScreen(X); DisplayAllFields; LastAction := EditForm(ActiveField); ActiveField := FieldWithFocus; case LastAction of Stop1: begin X := NdxGotoFirst; ActiveField := 13; { next } end; Stop2: begin X := NdxGotoPrev; if X = 0 then begin X := NdxGotoLast; PromptOK(' Top of file ','^Loop...') end; end; Stop3: begin X := NdxGotoNext; if X = 0 then begin X := NdxGotoFirst; PromptOK(' End of file ','^Loo...'); end; end; Stop4: begin X := NdxGotoLast; ActiveField := 12; { prev } end; end; { case } until LastAction = Escaped; DisposeFields; DisposeForms; Old TTTimers might be familiar with the TTT5 procedure ProcessInput. This procedure is still supported. The function FormExitAction can be called to determine gAction value used to end the edit session. Displaying The Form EditForm will automatically display the fields and labels in the form before commencing with the edit session. You can, however, display the form without passing control to the user by call the DisplayForm procedure, which is defined as follows: DisplayForm; Instructs Gold to display all the fields and labels associated with the active form. Behind the scenes, DisplayForm calls the two procedures DisplayAllLabels and DisplayAllFields. You can call these procedures individually if you prefer. For example, within an edit loop, you might want to call DisplayAllfields with each iteration of the loop because the data in the fields change each time around (see the code fragment on the previous page), but you might only call DisplayAllLabels once before starting the loop. Closing and Disposing of a Form Behind the scenes, forms are created on the heap in a custom form of linked list. You must dispose of the memory used by a form when the form is no longer required. The primary way to dispose of a form is to call the procedure DisposeFields. This procedure will remove all the field data from memory and effectively erase all information associated with the active table. If you have called DisposeFields for a table, you can (at a later time) redefine the table with a new set of fields. Using DisposeForms Gold maintains a list of information about each form, e.g. whether the form has any fields defined, is the form in a window, etc. When you have no further need for any of the forms, you can call DisposeForms to free every last drop of memory used by the forms. Using Hooks in Forms Brilliant though Gold is, there will be occasions when the form routines provided do not meet your exact requirements. There are six different custom hooks which will allow you to precisely customize a form's behavior to meet your specific needs. The hooks provide you with information about the current state of the form and the task being performed, and allow you to influence the execution of the task. For example, the user may be attempting to leave field 2 (perhaps by pressing the tab key or clicking with the mouse on another field). You could use a leave field hook to trap for the field change event, and only permit the user to leave field 2 when the data in the field is satisfactory. Gold provides the following form hooks: Character Hook Called every time the user presses a character. Hind Hook Called after a every character has been processed. Insert Hook Called when the user presses the insert key. Leave Field Hook Called every time the user tries to leave one field and move to another. Enter Fields Hook Called every time the user tries to enter a field, i.e. gives a field focus. Finished Hook Called when the user tries to close the form. Intercepting Every Character Two different hooks allow you to trap each character as it is processed. The character hook gives you a chance to review a character before it is processed by Gold, and the Hind hook is called after each character has been processed. Character Hook For a procedure to be eligible as a form character hook, it must adhere to the following rules: The function must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The function must be declared with three passed parameters. The first parameter is a variable word parameter which indicates the key which is about to be processed. The second parameter is a variable byte parameter, indicating the ID of the field with focus. The third parameter is also a variable of type byte which can be used to return a Refresh code, instructing Gold on how to proceed. The following procedure declaration follows these rules: {$F+} procedure KeyPressCheck(var C : word;var CF:byte; var Refresh:byte); begin If C = 315 then begin Help; C := 0; end; end; {$F-} The following procedure is then called to instruct Gold to call your procedure every time a character is about to be processed: AssignCharHook(Proc:CharHookProc); Here are a few tips about writing character hooks: If you want to trap for a special character (such as Alt-H), perform some custom task, and then stop Gold from trying to process this character, assign a value of zero to the character and Gold will ignore it. The Refresh parameter is used to instruct Gold to perform some special screen refreshing task. The constants (listed below) are declared in the GoldIO unit and should be used for this purpose. Assign one of these values to the third passed parameter if you want Gold to perform some special field refreshing operation, or if you want to end the edit session. RefreshNone = 0; RefreshCurrent = 1; RefreshAll = 2; RefreshOthers = 3; EndInput = 99; Hind Hook Like the character hook, the hind hook is called every time a key is pressed by the user. The hind hook, however, is called after the character has been processed by Gold. One use for a hind hook provides is to write some text to the screen based on the data entered into other fields; the sub-totals on an invoice form, for example. A hind hook is often used to change the enabled/disabled state of a field based on the value of other fields, e.g. if a font check box is not checked, then the hind hook might disable or hide the font list box. For a procedure to be eligible as a form character hook, it must adhere to the following rules: The function must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The function must be declared with two passed parameters. The first parameter is a byte parameter indicating the ID of the field with focus. The second parameter is a variable of type byte which can be used to return a Refresh code, instructing Gold on how to proceed. The following procedure declaration follows these rules: {$F+} procedure MyHindHook(CurrentField:byte;var Refresh:byte); {} begin Refresh := RefreshNone; if CurrentField = 9 then {radio button} begin if (Cust.Radio = 4) and (FieldGetState(10) = FldOn) then begin FieldSetState(10,FldOff); Refresh := RefreshOthers; end else if (Cust.Radio <> 4) and not (FieldGetState(10) = FldOn) then begin FieldSetState(10,FldOn); Refresh := RefreshOthers; end; end; end; {MyHindHook} {$F-} The following procedure is then called to instruct Gold to call your procedure every time a character has been processed: AssignHindHook(Proc:HindHookProc); The demo program DEMIO6.PAS shows a hind hook being used to enable and disable fields. Insert Hook The insert hook is called every time the user presses the insert key. By default, Gold sets the cursor to a full block in overtype mode, and an underscore in insert mode. The hook allows the programmer to introduce some other method of indicating the insert status, e.g. writing "insert" or "replace" somewhere on the screen. For a procedure to be eligible as a insert hook, it must adhere to the following rules: The function must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The function must be declared with one boolean parameter. This parameter indicates the new state of the insert key. The following procedure declaration adheres to these rules: {$F+} procedure MyInsHook(Insert:boolean); begin ... end; {$F-} The following procedure is then called to instruct Gold to call your procedure every time the insert key is pressed: AssignInsHook(Proc:InsProc); Field Changing Hooks Gold provides leave field and enter field hooks. These functions are called when the user tries to leave a field, and when the user tries to enter a field. The leave field hook provides a way to ensure that the contents of a field are valid before moving to another field. The enter field hook can be used to take some action before entering a field, e.g. display a warning message, or move the user to another field if a specific condition is not met. You declare leave field and enter field hooks in the same way, i.e. they accept the same arguments. For a procedure to be eligible as a leave or enter field hook, it must adhere to the following rules: The procedure must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The procedure must be declared with two variable byte parameters. The first parameter indicates the ID of the field which the user is about to leave or enter. The second parameter is a refresh code which may be updated to instruct Gold to either terminate the edit session or refresh one or more fields. You can use one of the following constants to indicate the appropriate refresh action: RefreshNone = 0; RefreshCurrent = 1; RefreshAll = 2; RefreshOthers = 3; EndInput = 99; One of the following two procedures can then be used to instruct Gold to call your procedure every time the user tries to leave or enter a field, respectively: AssignLeaveFieldHook(Proc:MoveFieldProc); AssignEnterFieldHook(Proc:MoveFieldProc); The Leave Field Hook As mentioned earlier, leave field hooks are often used to add some custom field's validation. For example, you might want to check that a filename field contains a valid filename. When you find that a field input is not valid, you can force the user to stay in the same field (i.e. stop changing focus) by setting the Field ID parameter to a value of zero. Listed below is an example of a leave field hook, which displays an error message and keeps the user in the same field when the field contains an odd number. (OK, it's a contrived example, but you get the idea!) {$F+} procedure MyLeaveHook(var CurrentField:byte;var Refresh:byte); {} begin if (CurrentField = 3) and odd(DressSize) then begin PromptOK(' Error ','The dress size must be an even number,e.g. 4,6,8...'); CurrentField := 0; end; end; {$F-} The Enter Field Hook An enter field hook is called every time the user changes focus to a new field. In fact, the hook is called just before the focus is changed, and so the hook can redirect the focus to a different field. If you want to direct the focus to a different field, just assign a new value to the Field ID parameter. Traditionally, enter field hooks were used to temporarily disable fields, i.e. to prevent a user from giving a field focus. Gold now offers the FieldSetState function as a quick and easy way to disable the field, so the primary need for an enter field hook has disappeared. Gold can display a context sensitive message when the user moves from field to field, but only a single line of text can be displayed. A good application for an enter field hook is to display custom multi-line help giving the user instructions relevant to the field with focus. The following code fragment shows how this could be done: {$F+} procedure MyEnterHook(var CurrentField:byte;var Refresh:byte); {} begin case CurrentField of 1: begin WriteAT(..... WriteAT(..... WriteAT(..... end; 3: begin WriteAT(..... WriteAT(..... WriteAT(..... end; ... end; {case} end; {$F-} The Finished Hook The leave field hook provides a way to implement custom validation on a field by field basis. The finish hook, however, provides a way to implement complete validation when the user tries to end the edit session (other than by escaping). The finish hook is called after all Gold's internal validation routines have been performed. It provides the user with one last chance to check all the edits before the form is closed. For a function to be eligible as a finished hook, it must adhere to the following rules: The function must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The function must be declared no passed parameters, and must return a byte value. The hook function returns the ID of the field which failed validation. If the function returns a zero, Gold will close the form. If the function returns a non-zero value, the edit session will continue and focus will shift to the Field whose ID matches the value returned by the hook function. Having declared the hook function, instruct Gold to call your procedure every time the user tries to close the form by calling the following procedure: AssignFinishedHook(Proc:FinishedProc); The following example shows a finished hook which makes sure that the first field is not null if the second field (a check box field) is checked. {$F+} function CheckNullState:byte; {} begin if (PrinterHeader = true) and (Heading = '') then begin PromptOK(' Error ','Heading Field cannot be empty when print is checked'); CheckNullState := 1 end else CheckNullState := 0; end; {$F+} Launching Forms on the Desktop All forms displayed on the desktop must be displayed in a window. However, in place of the procedure SetFormWindow (discussed earlier), a desktop form must be initialized with the LaunchFormInit function which is described below: LaunchFormInit(X1,Y1,X2,Y2,style:byte; CloseProc:FormCloseProc):byte; Initializes a form for use on the desktop. The first five parameters define the windows coordinates and style, respectively. The last parameter is the name of a function which will be called when the window containing the form is about to be removed from the desktop. LaunchFormInit returns the handle (or number) of the window which will contain the form. Once the form is initialized, the standard IO routines can be called in the normal manner, e.g. KwikAddField, DateField, etc. When the form has been fully defined, you launch the form (i.e. make it visible on the desktop) by calling the LaunchForm procedure which is described as follows: LaunchForm(StartField:byte); Displays the form window on the desktop. The single passed parameter identifies the FieldID of the field which will have focus when the form is first displayed. Understanding the FormCloseProc By design, a form launched on the desktop is non-modal, i.e. the user can change focus from a form to any other window on the desktop. The user may close the form at any time using a variety of methods. The user might click on the OK button on the form, click on the close icon, or select Close Window from the desktop menu. Since the desktop is event driven, you have to "wait" for the form to be closed before using the data entered into the form, calling DisposeFields and performing all the other necessary house-keeping duties. When you prepare a form for use on the desktop (using LaunchFormInit), you must specify the name of a function declared to be of type FormCloseProc, which is declared as follows: FormCloseProc = function(FormID: byte):boolean; This function will be called by Gold when the user attempts to close the form. This is your chance to do the housekeeping duties which you would normally perform after a call to EditForm (in a non-desktop application). The function returns a boolean to indicate to Gold whether the form may be closed. If a FALSE value is returned, the form window will not be closed. For a function to be eligible as a form close function it must adhere to the following rules: The function must be declared as a far function at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The function must be declared with a single passed parameter of type byte and return a boolean indicating whether the form can be closed. The passed parameter indicates the window handle (or number) of the window that is being closed. The following function declaration (extracted from DEMDESK4.PAS) follows these rules: {$F+} function FormShutDown(Form:byte):boolean; {} begin BarSetActive(MainMenu,200,true); DeskRefreshBgnd; DisposeFields; DisposeForms; PrintInfo.TypeSize := Longvar; FormShutDown := true; end; { FormShutDown } {$F-} Review the demo program DEMDESK4.PAS for a complete example of using forms in a desktop application. var Name,Tel:string; Age:byte; Sex: string[1]; Result: gAction; procedure SetScreen; {} begin ... end; { SetScreen } procedure SetVars; begin Name := ''; Tel := ''; Age := 0; Sex := ''; end; { SetVars } procedure SetFields; begin {create the 5 fields} KwikAddField(1, 25,9); {Field 1, column 25, line 9} KwikAddField(2, 25,11); {Field 2, column 25, line 11} KwikAddField(3, 25,13); {Field 3, column 25, line 13} KwikAddField(4, 25,15); {Field 4, column 25, line 15} KwikAddField(5, 26,18); {Field 5, column 25, line 18} KwikAddLastField(6, 43,18); {Field 6, column 43, line 18} {Field 1} StringField(1, Name, '******************************'); SetLabel(1, LabelLeft,LabelLeft,'Full name:'); {Field 2} StringField(2, Tel, '(###) ###-####'); SetLabel(2, LabelLeft,LabelLeft,'Tel:'); {Field 3} ByteField(3, Age, '##', 13,65); SetLabel(3, LabelLeft,LabelLeft,'Patients age:'); {Field 4} StringField(4, Sex, '!'); SetLabel(4, LabelLeft,LabelLeft,'Sex:'); {Fields 4 & 5: buttons} ButtonField(5,' OK ',finished); ButtonField(6,' Cancel ',escaped); end; { SetFields } begin SetScreen; SetVars; SetFields; SetValidation(ValidateAtEnd); MouseShow(true); Result := EditForm(1); GotoXY(1,25); DisposeFields; DisposeForms; ... end. Specify the field properties, and associate the field with a variable. Add labels to the fields. Add the fields to the form and define their order and location Initialize the variables that are going to be used in the form Run the form. Dispose of everything when the form is no longer needed.